package com.java.genericity;

import java.io.BufferedInputStream;
import java.util.Arrays;
import java.util.Scanner;

/**
 * @program: java_basic_knowledge
 * @description:
 * @author: CaoYong
 * @create: 2021-04-08 09:59
 **/
public class GenericityKnowledge {

    public static void main(String[] args) {
        whatIsGenericity();
        Scanner in = new Scanner(System.in);
        String next = in.next();
//        UseGenericity demo = make("hello", 1);
//        System.out.println(demo.first.getClass());
//        System.out.println(demo.second.getClass());
    }




    /**
     * 对于泛型类，Java编译器会将泛型代码转换为普通的非泛型代码，就类似于将泛型参数T擦除替换为Object并插入相应的强制类型转换
     * Java在执行时并不知道泛型而是只知道普通类
     * 使用泛型相对于使用Object而言有更好的安全性和可读性
     */
    public static void whatIsGenericity() {
        UseGenericity<String, Integer> useGenericity = new UseGenericity<>("张三", 11);
        NotUseGenericity notUseGenericity = new NotUseGenericity("张三", 11);
        String first = useGenericity.getFirst();
        Integer second = useGenericity.getSecond();

        String first1 = (String)notUseGenericity.getFirst();
        Integer second1 = (Integer)notUseGenericity.getSecond();

        System.out.println(first.equals(first1));
        System.out.println(second.equals(second1));
    }



    /**
     * 泛型方法
     * @param arr
     * @param param
     * @param <T>
     * @return
     */
    public static <T> int genericityMethod(T[] arr, T param) {
        for (int i = 0; i < arr.length; i++) {
            if(arr[i].equals(param)) {
                return i;
            }
        }

        return -1;
    }

    public static <U, V> UseGenericity<U, V> make(U first, V second) {
        UseGenericity<U, V> demo = new UseGenericity(first, second);
        return demo;
    }

    public static void genericityWildCard() {
        DynamicArray<Number> numbers = new DynamicArray<>();
        DynamicArray<Integer> ints = new DynamicArray<>();
        ints.add(111);
        ints.add(222);
        numbers.addAll(ints); // 提示编译错误，这是因为Integer是Number的子类，但是DynamicArray<Integer>并不是DynamicArray<Number>的子类， 可以通弄类型限定解决这个问题

        DynamicArray<? extends Number> array = ints;
        Integer a = 200;
//        array.add(a); // 编译错误 通配符有一个重要的限制就是只能读，不能写， 因为? extends Number并不知道其实际的类型，如果允许写则无法保证安全性
//        array.add((Number) a);

    }

    /**
     * 对于泛型一般擦除后为Object， 但是Java支持为这个类型参数限定一个参数的上界，也就是是参数类型必须为给定类型或者其子类型，通过extends关键字表示
     * @param <T>
     * @param <V>
     */
    public static class LimitGenericity<T extends Number, V extends Number> extends UseGenericity<T, V> {

        public LimitGenericity(T first, V second) {
            super(first, second);
        }
    }

    /**
     * 使用泛型可以让代码的功能与使用的数据类型分离，使得同一套代码可以用于多种数据类型，提高代码的可复用性
     * @param <T>
     */
    public static class UseGenericity<T, V> {

        // T 代表类型参数，泛型就是类型的参数化
        T first;
        V second;


        public UseGenericity(T first, V second) {
            this.first = first;
            this.second = second;
        }

        public T getFirst() {
            return first;
        }

        public void setFirst(T first) {
            this.first = first;
        }

        public V getSecond() {
            return second;
        }

        public void setSecond(V second) {
            this.second = second;
        }
    }

    public static class NotUseGenericity {
        Object first;
        Object second;

        public NotUseGenericity(Object first, Object second) {
            this.first = first;
            this.second = second;
        }

        public Object getFirst() {
            return first;
        }

        public void setFirst(Object first) {
            this.first = first;
        }

        public Object getSecond() {
            return second;
        }

        public void setSecond(Object second) {
            this.second = second;
        }
    }

    public static class DynamicArray<E> {
        private static final int DEFAULT_CAPACITY = 10;
        private int size;
        private Object[] elementData;

        public DynamicArray() {
            this.elementData = new Object[DEFAULT_CAPACITY];
        }

        private void ensureCapacity(int minCapacity) {
            int oldCapacity = elementData.length;
            if (oldCapacity >= minCapacity) {
                return;
            }
            int newCapacity = oldCapacity * 2;
            if (newCapacity < minCapacity) {
                newCapacity = minCapacity;
                elementData = Arrays.copyOf(elementData, newCapacity);
            }
        }

        public void add(E e) {
            ensureCapacity(size + 1);
            elementData[size ++ ] = e;
        }

        public E get(int index) {
            return (E)elementData[index];
        }

//        public <T extends E> void addAll(DynamicArray<T> c) {
//            for (int i = 0; i < c.size; i++) {
//                add(c.get(i));
//            }
//        }

        // 对于上面的式子也可以通过通配符替代
        // <T extends E> 用于定义类型参数，表明一个类型参数T，可放在泛型类类名后面，泛型方法返回值前面
        // <? extends E> 用于实例化类型参数, 用于实例化泛型变量中的类型参数，只不过具体类型是未知的，只知道它是E或E的子类
        public  void addAll(DynamicArray<? extends E> c) {
            for (int i = 0; i < c.size; i++) {
                add(c.get(i));
            }
        }
    }
}
